package com.tjc.spring.core;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

/**
 * @Author: tjc
 * @description
 * @Date Created in 14:52 2022-01-02
 */
public class PropertyResolver {
    private BeanFactory beanFactory;

    public PropertyResolver(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    public Object autowirePorperty(Object bean, GenericBeanDefinition bd) {
        List<Property> properties = bd.getPropertys();
        Class<?> beanClass = bd.getClazz();
        BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory);
        //
        Method[] candidates = beanClass.getMethods();
        Method[] methodToUse = new Method[properties.size()];
        Object[] argsToUse = new Object[properties.size()];
        SimpleTypeConverter typeConverter = new SimpleTypeConverter();
        for (int i = 0; i < properties.size(); i++) {
            Property property = properties.get(i);
            boolean result = this.matchType(property, candidates, methodToUse, argsToUse, valueResolver, typeConverter);
            if (result) {
                break;
            }
        }

        try {
            for (int i = 0; i < methodToUse.length; i++) {
                methodToUse[i].invoke(bean, argsToUse[i]);
            }
        } catch (InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException(bd.getID() + "can't inject property");
        }
        return bean;
    }

    private boolean matchType(Property property, Method[] candidates, Method[] methodToUse, Object[] argsToUse, BeanDefinitionValueResolver valueResolver,
                              SimpleTypeConverter typeConverter) {
        String propertyName = property.getName();
        Object originValue = property.getValue();
        Object resolveValue = valueResolver.resolveValueIfNecessary(originValue);
        Object convertValue = null;
        int i = 0;
        for (Method candidate : candidates) {
            Class<?>[] parameterTypes = candidate.getParameterTypes();
            if (parameterTypes.length > 1) {
                continue;
            }
            if (!matchMethodName(propertyName, candidate.getName())) {
                continue;
            }
            try {
                convertValue = typeConverter.convertIfNecessary(resolveValue, parameterTypes[0]);
            } catch (Exception e) {
                continue;
            }
            argsToUse[i] = convertValue;
            methodToUse[i] = candidate;
            i++;
            return true;
        }
        return false;
    }

    private boolean matchMethodName(String propertyName, String methodName) {
        String upperCamelCasePropName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
        return methodName.equals("set" + upperCamelCasePropName);
    }
}
